#include <jni.h>

#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <signal.h>

#include "entry.h"


#ifndef NELEM
#define NELEM(x) ((int)(sizeof(x) / sizeof((x)[0])))
#endif

int g_isEnableDebug = 1;

JavaVM		*g_vm;
JNIEnv		*g_env;
jobject		g_application, g_asset, g_classLoader;
const char	*g_dex_path, *g_pkg;


static const char *s_class_path_name = "android/support/v4/app/e";


static JNINativeMethod s_methods[] = {
	{"check", "(Ljava/lang/String;)I", (void*) native_check},
	{"load", "()I", (void*) native_load_core},
	{"findClass", "(Ljava/lang/String;)Ljava/lang/Class;", (void*) native_find_class},
	{"boot", "()I", (void*) native_boot},
	{"getData", "()Ljava/lang/String;", (void*) native_get_data},
	{"encrypt", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", (void*) native_encrypt},
	{"encrypt1", "(Ljava/lang/String;)Ljava/lang/String;", (void*) native_encrypt1},
	{"decrypt", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", (void*) native_decrypt},
	{"decrypt1", "(Ljava/lang/String;)Ljava/lang/String;", (void*) native_decrypta},
	{"decrypt3", "(Ljava/lang/String;)Ljava/lang/String;", (void*) native_decryptc}
};


static int register_native_methods(JNIEnv* env,
		const char* class_name,
		JNINativeMethod* methods,
		int num_methods)
{
	jclass clazz;

	clazz = (env)->FindClass( class_name);
	if (clazz == NULL) {
		log_error("Native registration unable to find class '%s'\n",
				class_name);
		return JNI_FALSE;
	}
	if ((env)->RegisterNatives( clazz, methods, num_methods) < 0) {
		log_error("RegisterNatives failed for '%s'\n", class_name);
		return JNI_FALSE;
	}

	return JNI_TRUE;
}

const char *jstr2cstr(JNIEnv *env, jstring js)
{
	return env->GetStringUTFChars(js, NULL);
}

static int register_natives(JNIEnv *env)
{
	if(!register_native_methods(env, s_class_path_name, s_methods, NELEM(s_methods)))
		return JNI_FALSE;

	jclass cls = (env)->FindClass("android/app/ActivityThread");
	jmethodID id = (env)->GetStaticMethodID( cls, "currentApplication", "()Landroid/app/Application;");
	jobject obj = (env)->CallStaticObjectMethod( cls, id);
	g_application = (env)->NewGlobalRef( obj);
	env->DeleteLocalRef(obj);

	cls = (env)->FindClass("android/content/ContextWrapper");
	id = env->GetMethodID(cls, "getAssets", "()Landroid/content/res/AssetManager;");
	obj = env->CallObjectMethod(g_application, id);
	g_asset = env->NewGlobalRef(obj);
	env->DeleteLocalRef(obj);

	id = env->GetMethodID(cls, "getPackageName", "()Ljava/lang/String;");
	obj = env->CallObjectMethod(g_application, id);
	g_pkg = jstr2cstr(env, (jstring)obj);
	env->DeleteLocalRef(obj);

	//File getDir(String name, int mode)
	id = env->GetMethodID(cls, "getDir", "(Ljava/lang/String;I)Ljava/io/File;");
	obj = env->CallObjectMethod(g_application, id, env->NewStringUTF("dex"), 0);
	//String getPath()
	jclass cls2 = env->FindClass("java/io/File");
	id = env->GetMethodID(cls2, "getPath", "()Ljava/lang/String;");
	jobject path = env->CallObjectMethod(obj, id);
	g_dex_path = jstr2cstr(env, (jstring)path);
	env->DeleteLocalRef(obj);
	env->DeleteLocalRef(path);

	id = env->GetMethodID(cls, "getClassLoader", "()Ljava/lang/ClassLoader;");
	obj = env->CallObjectMethod(g_application, id);
	g_classLoader = env->NewGlobalRef(obj);
	env->DeleteLocalRef(obj);

//	native_load_core(env, NULL);

	return JNI_TRUE;
}

jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved)
{
	JNIEnv* env = NULL;
	jint result = -1;

	log_info("hello jni!");

	g_vm = vm;

	if ((vm)->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
		log_error("ERROR: GetEnv failed\n");
		goto bail;
	}
	assert(env != NULL);

	g_env = env;

	if (register_natives(env) < 0) {
		log_error("ERROR: Exif native registration failed\n");
		goto bail;
	}

	/* success -- return valid version number */
	result = JNI_VERSION_1_4;

bail:
	return result;
}

